package task3;

import java.sql.Connection;
import java.util.Date;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

public class ConnectionPool {

	private final Semaphore semaphore;
	
	private final Queue<Connection> connections;
	private final Queue<Connection> givenConnections;
	
	private final static int WAITING_TIMEOUT = 4000;
	private final static int IDLE_TIMEOUT = 3000;
	
	private final Thread idleChecker;
	
	private int poolSize;
	
	public ConnectionPool(String url, String user, String password, int poolSize){
		this.poolSize = poolSize;
		semaphore = new Semaphore(poolSize);
		connections = new ArrayBlockingQueue<Connection>(poolSize);
		givenConnections = new ArrayBlockingQueue<Connection>(poolSize);
		instantiateConnections(url, user, password);
		idleChecker = new Thread(new IdleChecker());
		idleChecker.setDaemon(true);
		idleChecker.start();
	}
	
	private void instantiateConnections(String url, String user,
			String password) {
		for(int i = 0; i < poolSize; i++){
			connections.add(new ConnectionStub(IDLE_TIMEOUT));
		}
	}

	public Connection checkout() throws InterruptedException {
		if(semaphore.tryAcquire(WAITING_TIMEOUT, TimeUnit.MILLISECONDS)){
			Connection connection = connections.poll();
			givenConnections.add(connection);
			return connection;
		} else {
			throw new InterruptedException("WAITING TIMEOUT EXPIRED!");
		}
	}
	
	public void checkin(Connection connection) {
		try{
	        connections.add(connection);
	        givenConnections.remove(connection);
		} finally {
	        semaphore.release();
		}
	}
	
	class IdleChecker implements Runnable{

		private final int frequency = 1500;
		
		public void run(){
			while(true){
				System.out.println("Looking through given connections");
				for(Connection connection : givenConnections){
					if(connection instanceof ConnectionStub){
						long needToBeTaken = ((ConnectionStub)connection).lastUsed.getTime() + IDLE_TIMEOUT;
						long now = new Date().getTime();
						System.out.println(now + " - " + needToBeTaken + " = " + (now - needToBeTaken));
						if(needToBeTaken <
								now){
							System.out.println("Getting connection back due to idle time");
							checkin(connection);
						}
					}
				}
				
				try {
					Thread.sleep(frequency);
				} catch (InterruptedException e) {
					System.out.println(e.getMessage());
				}
			}
		}
	}
	
	//test
	public static void main(String[] str) throws InterruptedException{
		
		ConnectionPool pool = new ConnectionPool("", "", "", 4);
		
		int users = 5;
		ExecutorService executor = Executors.newCachedThreadPool();
		for(int i = 0; i < users; i++){
			executor.execute(new ConnectionUser(pool));
		}
		
		Thread.sleep(30000);
		executor.shutdown();
	}
}
